package asruby.lang
{
import asruby.runtime.methods.FullFunctionCallbackMethod;
import asruby.Ruby;
import asruby.parser.StaticScope;
import asruby.runtime.Arity;
import asruby.runtime.Block;
import asruby.runtime.CacheMap;
import asruby.runtime.CallbackFactory;
import asruby.runtime.ClassIndex;
import asruby.runtime.Dispatcher;
import asruby.runtime.IRubyObject;
import asruby.runtime.MethodFactory;
import asruby.runtime.MethodIndex;
import asruby.runtime.ObjectAllocator;
import asruby.runtime.ThreadContext;
import asruby.runtime.VariableTableEntry;
import asruby.runtime.Visibility;
import asruby.runtime.callback.Callback;
import asruby.runtime.methods.AliasMethod;
import asruby.runtime.methods.DynamicMethod;
import asruby.runtime.methods.SimpleCallbackMethod;
import asruby.runtime.methods.UndefinedMethod;
import asruby.util.ClassProvider;
import asruby.util.IdUtil;
import asruby.anno.JRubyMethod;

import flash.utils.Dictionary;
	
public class RubyModule extends RubyObject {
    
    public static function createModuleClass(runtime: Ruby, moduleClass: RubyClass): RubyClass {
        moduleClass.index = ClassIndex.MODULE;
        moduleClass.kindOf = function  isKindOf(obj: IRubyObject, type: RubyModule): Boolean {
                return obj instanceof RubyModule;
            };
        
        var callbackFactory: CallbackFactory = runtime.callbackFactory(RubyModule);
        
        moduleClass.defineAnnotatedMethods(RubyModule);
        moduleClass.dispatcher = callbackFactory.createDispatcher(moduleClass);
// Note: ToDO
/*         callbackFactory = runtime.callbackFactory(RubyKernel);
        moduleClass.defineFastMethod2("autoload", callbackFactory.getFastSingletonMethod("autoload", RubyKernel.IRUBY_OBJECT, RubyKernel.IRUBY_OBJECT));
        moduleClass.defineFastMethod2("autoload?", callbackFactory.getFastSingletonMethod("autoload_p", RubyKernel.IRUBY_OBJECT));
 */
        return moduleClass;
    }    
    
    public static var MODULE_ALLOCATOR:Function = function (runtime: Ruby, klass: RubyClass): IRubyObject {
        return new RubyModule(runtime, klass);
    }
    
    public override function getNativeTypeIndex(): int {
        return ClassIndex.MODULE;
    }

    public override function  isModule(): Boolean {
        return true;
    }

    public override function  isClass(): Boolean {
        return false;
    }

    public function  isSingleton(): Boolean {
        return false;
    }    
    
    // superClass may be null.
    protected var superClass: RubyClass;

    public var index: int;
    
    public var dispatcher: Dispatcher = Dispatcher.DEFAULT_DISPATCHER;

/*     public static classvar  :  ;
        public static  var DEFAULT_KIND_OF: KindOf = new KindOf();
        public function  isKindOf(obj: IRubyObject, type: RubyModule): Boolean {
            return obj.getMetaClass().hasModuleInHierarchy(type);
        }
    }
   */
    public function  isInstance(object: IRubyObject): Boolean {
        return kindOf(object, this);
    }

    public var kindOf: Function = 
    	function  isKindOf(obj: IRubyObject, type: RubyModule): Boolean {
            return obj.getMetaClass().hasModuleInHierarchy(type);
        };
    
    public var _id: int;

    // Containing class...The parent of Object is null. Object should always be last in chain.
    public var parent: RubyModule;

    // ClassId is the name of the class/module sans where it is located.
    // If it is null, then it an anonymous class.
    public var classId: String;


    // CONSTANT TABLE
        
    protected var constantTable: Array = new Array();
    
    private var methods: Dictionary = new Dictionary();
    
    // ClassProviders return Java class/module (in #defineOrGetClassUnder and
    // #defineOrGetModuleUnder) when class/module is opened using colon syntax. 
    private var classProviders: Array;

    /** separate path for MetaClass construction
     * 
     */
	function RubyModule( runtime:Ruby, metaClass:RubyClass, objectSpace:Boolean=false) {
        super(runtime, metaClass, objectSpace);
        _id = runtime.allocModuleId();
        // if (parent == var parent: null = runtime.getObject();
        setFlag(USER7_F, !isClass());
    }
    
    /** used by MODULE_ALLOCATOR and RubyClass constructors
     * 
     */
/*     protected RubyModule(Ruby runtime, RubyClass metaClass) {
        this(runtime, metaClass, runtime.isObjectSpaceEnabled());
    } */
    
    /** standard path for Module construction
     * 
     */
/*     protected RubyModule(Ruby runtime) {
        this(runtime, runtime.getModule());
    } */

    public function  needsImplementer(): Boolean {
        return getFlag(USER7_F);
    }
    
    /** rb_module_new
     * 
     */
    public static function  newModule(runtime: Ruby): RubyModule {
        return new RubyModule(runtime);
    }
    
    /** rb_module_new/rb_define_module_id/rb_name_class/rb_set_class_path
     * 
     */
    public static function  newModule2(runtime: Ruby, name: String, parent: RubyModule, setParent: Boolean): RubyModule {
        var module: RubyModule = newModule(runtime);
        module.setBaseName(name);
        if (setParent) module.setParent(parent);
        parent.setConstant(name, module);
        return module;
    }
    
    // synchronized method per JRUBY-1173 (unsafe Double-Checked Locking)
    // FIXME: synchronization is still wrong in CP code
    public function  addClassProvider(provider: ClassProvider): void {
        if (classProviders == null) {
            var cp: Array = new Array();
            cp.add(provider);
            classProviders = cp;
        } else {        
            if (!classProviders.contains(provider)) {
                classProviders.add(provider);
            }         
        }
    }

    public function  removeClassProvider(provider: ClassProvider): void {
        if (classProviders != null) {
            classProviders.remove(provider);
        }
    }

    private function  searchProvidersForClass(name: String, superClazz: RubyClass): RubyClass {
        if (classProviders != null) {           
            var clazz:RubyClass;
            for (var classProvider:ClassProvider in classProviders) {
                if ((clazz = classProvider.defineClassUnder(this, name, superClazz))!= null) {
                    return clazz;
                }
            }
            
        }
        return null;
    }

    private function  searchProvidersForModule(name: String): RubyModule {
        if (classProviders != null) {           
            var module:RubyModule;
            for (var classProvider:ClassProvider in classProviders) {
                if ((module = classProvider.defineModuleUnder(this, name)) != null) {
                    return module;
                }
            }
            
        }
        return null;
    }

    public function  getDispatcher(): Dispatcher {
        return dispatcher;
    }

    /** Getter for property superClass.
     * @return Value of property superClass.
     */
    public function  getSuperClass(): RubyClass {
        return superClass;
    }

    public function setSuperClass( superClass:RubyClass):void  {
        this.superClass = superClass;
    }

    public function  getParent(): RubyModule {
        return parent;
    }

    public function  setParent(parent: RubyModule): void {
        this.parent = parent;
    }

    public function  getMethods(): Dictionary {
        return methods;
    }
    

    // note that addMethod now does its own put, so any change made to
    // functionality here should be made there as well 
    private function  putMethod(name: String, method: DynamicMethod): void {
        // FIXME: kinda hacky...flush STI here
        dispatcher.clearIndex(MethodIndex.getIndex(name));
        getMethods().put(name, method);
    }

    /**
     * Is this module one that in an included one (e.g. an IncludedModuleWrapper). 
     */
    public function  isIncluded(): Boolean {
        return false;
    }

    public function  getNonIncludedClass(): RubyModule {
        return this;
    }

    public function  getBaseName(): String {
        return classId;
    }

    public function  setBaseName(name: String): void {
        classId = name;
    }

    /**
     * Generate a fully-qualified class name or a #-style name for anonymous and singleton classes.
     * 
     * Ruby C equivalent = "classname"
     * 
     * @return The generated class name
     */
    public function  getName(): String {
        if (getBaseName() == null) {
            if (isClass()) {
                return this.toString();//"#<" + "Class" + ":01x" + Number.toHexString(System.identityHashCode(this)) + ">";
            } else {
                return this.toString();//"#<" + "Module" + ":01x" + Number.toHexString(System.identityHashCode(this)) + ">";
            }
        }

        var result: String = new String(getBaseName());
        var objectClass: RubyClass = getRuntime().getObject();

        for (var p:RubyModule = this.getParent(); p != null && p != objectClass; p = p.getParent()) {
            var pName: String = p.getBaseName();
            // This is needed when the enclosing class or module is a singleton.
            // In that case, we generated a name such as null::Foo, which broke 
            // Marshalling, among others. The correct thing to do in this situation 
            // is to insert the generate the name of form #<Class:01xasdfasd> if 
            // it's a singleton module/class, which this code accomplishes.
            if(pName == null) {
                pName = p.getName();
            }
            result.insert(0, "::").insert(0, pName);
        }

        return result.toString();
    }

    /**
     * Create a wrapper to use for including the specified module into this one.
     * 
     * Ruby C equivalent = "include_class_new"
     * 
     * @return The module wrapper
     */
/*     public function  newIncludeClass(superClazz: RubyClass): IncludedModuleWrapper {
        var includedModule: IncludedModuleWrapper = new IncludedModuleWrapper(getRuntime(), superClazz, this);

        // include its parent (and in turn that module's parents)
        if (getSuperClass() != null) {
            includedModule.includeModule(getSuperClass());
        }

        return includedModule;
    } */
    /**
     * Finds a class that is within the current module (or class).
     * 
     * @param name to be found in this module (or class)
     * @return the class or null if no such class
     */
    public function  getClass(name: String): RubyClass {
        var module:IRubyObject;
 		if ((module = getConstantAt(name)) instanceof RubyClass) {
            return RubyClass(module);
        }
        return null;
    }

    public function  fastGetClass(internedName: String): RubyClass {
        var module:IRubyObject;
 		if ((module = fastGetConstantAt(internedName)) instanceof RubyClass) {
            return RubyClass(module);
        }
        return null;
    }

    /**
     * Include a new module in this module or class.
     * 
     * @param arg The module to include
     */
    public function  includeModule(arg: IRubyObject): void {
        //assert arg != null;

        //testFrozen("module");
        if (!isTaint()) {
            getRuntime().secure(4);
        }

        if (!(arg instanceof RubyModule)) {
            throw getRuntime().newTypeError("Wrong argument type " + arg.getMetaClass().getName() +
                    " (expected Module).");
        }

        var module:RubyModule = RubyModule(arg);

        // Make sure the module we include does not already exist
        if (isSame(module)) {
            return;
        }

        //infectBy(module);

        doIncludeModule(module);
    }

    public function  defineMethod(name: String, method: Callback): void {
        var visibility: Visibility = (name == "initialize") ?
                Visibility.PRIVATE : Visibility.PUBLIC;
        addMethod(name, new FullFunctionCallbackMethod(this, method, visibility));
    }
    
    public function  defineAnnotatedMethod(clazz: Class, name: String): void {
        // FIXME: This is probably not very efficient, since it loads all methods for each call
        var foundMethod: Boolean = false;
        for (var method:RubyMethod in clazz.getDeclaredMethods()) {
            if ( (method.getName() == name) && 
                 defineAnnotatedMethod(method, MethodFactory.createFactory(getRuntime().getJRubyClassLoader()))) {
                foundMethod = true;
            }
        }

        if (!foundMethod) {
            throw new RuntimeException("No JRubyMethod present for method " + name + "on class " + clazz.getName());
        }
    }
    
    public function  defineAnnotatedMethods(clazz: Class): void {
        if (RubyInstanceConfig.INDEXED_METHODS) {
            defineAnnotatedMethodsIndexed(clazz);
        } else {
            defineAnnotatedMethodsIndividually(clazz);
        }
    }
    
    public function  defineAnnotatedMethodsIndividually(clazz: Class): void {
        var declaredMethods: Array = clazz.getDeclaredMethods();
        var methodFactory: MethodFactory = MethodFactory.createFactory(getRuntime().getJRubyClassLoader());
        for (var method: RubyMethod in declaredMethods) {
            defineAnnotatedMethod(method, methodFactory);
        }
    }
/*    
    private function  defineAnnotatedMethodsIndexed(clazz: Class): void {
        var methodFactory: MethodFactory = MethodFactory.createFactory(getRuntime().getJRubyClassLoader());
        methodFactory.defineIndexedAnnotatedMethods(this, clazz, methodDefiningCallback);
    }
   
    private static var methodDefiningCallback: MethodFactory.MethodDefiningCallback = new MethodFactory.MethodDefiningCallback(
        function  define(module: RubyModule, method:Method, dynamicMethod: DynamicMethod): void {
            var jrubyMethod: JRubyMethod = method.getAnnotation(jrubyMethod);
            if (jrubyMethod.frame()) {
                for (var name :String in jrubyMethod.name()) {
                    ASTInspector.FRAME_AWARE_METHODS.add(name);
                }
            }
            if(jrubyMethod.compat() == CompatVersion.BOTH ||
                    module.getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
                var metaClass: RubyModule = module.metaClass;

                if (jrubyMethod.meta()) {
                    var baseName:String;
                    if (jrubyMethod.name().length == 0) {
                        baseName = method.getName();
                        metaClass.addMethod(baseName, dynamicMethod);
                    } else {
                        baseName = jrubyMethod.name()[0];
                        for (var name :String in jrubyMethod.name()) {
                            metaClass.addMethod(name, dynamicMethod);
                        }
                    }

                    if (jrubyMethod.alias().length > 0) {
                        for (var alias : String in jrubyMethod.alias()) {
                            metaClass.defineAlias(alias, baseName);
                        }
                    }
                } else {
                    var baseName:String;
                    if (jrubyMethod.name().length == 0) {
                        baseName = method.getName();
                        module.addMethod(method.getName(), dynamicMethod);
                    } else {
                        baseName = jrubyMethod.name()[0];
                        for (var name : String in jrubyMethod.name()) {
                            module.addMethod(name, dynamicMethod);
                        }
                    }

                    if (jrubyMethod.alias().length > 0) {
                        for (var alias :String in jrubyMethod.alias()) {
                            module.defineAlias(alias, baseName);
                        }
                    }

                    if (jrubyMethod.module()) {
                        // module/singleton methods are all defined public
                        var moduleMethod: DynamicMethod = dynamicMethod.dup();
                        moduleMethod.setVisibility(Visibility.PUBLIC);

                        var singletonClass: RubyModule = module.getSingletonClass();

                        if (jrubyMethod.name().length == 0) {
                            baseName = method.getName();
                            singletonClass.addMethod(method.getName(), moduleMethod);
                        } else {
                            baseName = jrubyMethod.name()[0];
                            for (var name :String in jrubyMethod.name()) {
                                singletonClass.addMethod(name, moduleMethod);
                            }
                        }

                        if (jrubyMethod.alias().length > 0) {
                            for (var alias :String in jrubyMethod.alias()) {
                                singletonClass.defineAlias(alias, baseName);
                            }
                        }
                    }
                }
            }
        }
    );
    
    public function  defineAnnotatedMethod2(method: Function, methodFactory: MethodFactory): Boolean {
        var jrubyMethod: JRubyMethod = method.getAnnotation(JRubyMethod);

        if (jrubyMethod == null) return false;

            if(jrubyMethod.compat() == CompatVersion.BOTH ||
                    getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
            var dynamicMethod: DynamicMethod = methodFactory.getAnnotatedMethod(this, method);
            methodDefiningCallback.define(this, method, dynamicMethod);
            
            return true;
        }
        return false;
    }
*/
    public function  defineFastMethod(name: String, method: Callback): void {
        var visibility: Visibility = (name == "initialize") ?
                Visibility.PRIVATE : Visibility.PUBLIC;
        addMethod(name, new SimpleCallbackMethod(this, method, visibility));
    }

    public function  defineFastMethod2(name: String, method: Callback, visibility: Visibility): void {
        addMethod(name, new SimpleCallbackMethod(this, method, visibility));
    }

    public function  definePrivateMethod(name: String, method: Callback): void {
        addMethod(name, new FullFunctionCallbackMethod(this, method, Visibility.PRIVATE));
    }

    public function  defineFastPrivateMethod(name: String, method: Callback): void {
        addMethod(name, new SimpleCallbackMethod(this, method, Visibility.PRIVATE));
    }

    public function  defineFastProtectedMethod(name: String, method: Callback): void {
        addMethod(name, new SimpleCallbackMethod(this, method, Visibility.PROTECTED));
    }

    public function  undefineMethod(name: String): void {
        addMethod(name, UndefinedMethod.getInstance());
    }

    /** rb_undef
     *
     */
    public function  undef(context: ThreadContext, name: String): void {
        var runtime: Ruby = getRuntime();
        if (this == runtime.getObject()) {
            runtime.secure(4);
        }
        if (runtime.getSafeLevel() >= 4 && !isTaint()) {
            throw new SecurityException("Insecure: can't undef");
        }
        //testFrozen("module");
        if ( (name == "__id__") || ( name == "__send__")) {
            getRuntime().getWarnings().warn(RubyWarnings.UNDEFINING_BAD, "undefining `"+ name +"' may cause serious problem");
        }
        var method: DynamicMethod = searchMethod(name);
        if (method.isUndefined()) {
            var s0:String = " class";
            var c: RubyModule = this;

            if (c.isSingleton()) {
                var obj:IRubyObject = c.getAttached();

                if (obj != null && obj instanceof RubyModule) {
                    c = obj;
                    s0 = "";
                }
            } else if (c.isModule()) {
                s0 = " module";
            }

            throw getRuntime().newNameError("Undefined method " + name + " for" + s0 + " '" + c.getName() + "'", name);
        }
        addMethod(name, UndefinedMethod.getInstance());
        
        if (isSingleton()) {
            var singleton:IRubyObject = this.getAttached(); 
            singleton.callMethod2(context, "singleton_method_undefined", getRuntime().newSymbol(name));
        } else {
            callMethod2(context, "method_undefined", getRuntime().newSymbol(name));
        }
    }
    
    //@JRubyMethod(name = var required: "include?" = 1)
    public function  include_p(arg: IRubyObject): IRubyObject {
        if (!arg.isModule()) {
            throw getRuntime().newTypeError(arg, getRuntime().getModule());
        }
        
        for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            if ((p instanceof IncludedModuleWrapper) && (p.getNonIncludedClass() == arg) ) {
                return getRuntime().newBoolean(true);
            }
        }
        
        return getRuntime().newBoolean(false);
    }

    // TODO: Consider a better way of synchronizing 
    public function  addMethod(name: String, method: DynamicMethod): void {
        /*if (this == getRuntime().getObject()) {
            getRuntime().secure(4);
        }

        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError("Insecure: can't define method");
        }
        testFrozen("class/module");
        */
        // We can safely reference methods here instead of doing getMethods() since if we
        // are adding we are not using a IncludedModuleWrapper.
    
        // If we add a method which already is cached in this class, then we should update the 
        // cachemap so it stays up to date.
        var existingMethod: DynamicMethod = getMethods().put(name, method);
        if (existingMethod != null) {
            getRuntime().getCacheMap().remove(existingMethod);
        }
        // note: duplicating functionality from putMethod, since we
        // remove/put atomically here
        dispatcher.clearIndex(MethodIndex.getIndex(name));
        
    }

    public function  removeMethod(context: ThreadContext, name: String): void {
        if (this == getRuntime().getObject()) {
            getRuntime().secure(4);
        }
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError("Insecure: can't remove method");
        }
        testFrozen("class/module");

        // We can safely reference methods here instead of doing getMethods() since if we
        // are adding we are not using a IncludedModuleWrapper.
     
        var method:DynamicMethod = getMethods().remove(name);
        if (method == null) {
            throw getRuntime().newNameError("method '" + name + "' not defined in " + getName(), name);
        }
        
        getRuntime().getCacheMap().remove(method);
        
        
        if(isSingleton()){
            var singleton:IRubyObject = this.getAttached(); 
            singleton.callMethod2(context, "singleton_method_removed", getRuntime().newSymbol(name));
        } else {
            callMethod2(context, "method_removed", getRuntime().newSymbol(name));
    }
    }

    /**
     * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
     * 
     * @param name The name of the method to search for
     * @return The method, or UndefinedMethod if not found
     */
    public function  searchMethod(name: String): DynamicMethod {
        for (var searchModule:RubyModule = this; searchModule != null; searchModule = searchModule.getSuperClass()) {
            // See if current class has method or if it has been cached here already
            var method:DynamicMethod = searchModule.getMethods().get(name);

            if (method != null) {
                return method;
            }
        }

        return UndefinedMethod.getInstance();
    }

    /**
     * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
     * 
     * @param name The name of the method to search for
     * @return The method, or UndefinedMethod if not found
     */
    public function  retrieveMethod(name: String): DynamicMethod {
        return getMethods().get(name);
    }

    /**
     * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
     * 
     * @param name The name of the method to search for
     * @return The method, or UndefinedMethod if not found
     */
    public function  findImplementer(clazz: RubyModule): RubyModule {
        for (var searchModule:RubyModule = this; searchModule != null; searchModule = searchModule.getSuperClass()) {
            if (searchModule.isSame(clazz)) {
                return searchModule;
            }
        }

        return null;
    }

    public function  addModuleFunction(name: String, method: DynamicMethod): void {
        addMethod(name, method);
        getSingletonClass().addMethod(name, method);
    }

    /** rb_define_module_function
     *
     */
    public function  defineModuleFunction(name: String, method: Callback): void {
        definePrivateMethod(name, method);
        getSingletonClass().defineMethod(name, method);
    }

    /** rb_define_module_function
     *
     */
    public function  definePublicModuleFunction(name: String, method: Callback): void {
        defineMethod(name, method);
        getSingletonClass().defineMethod(name, method);
    }

    /** rb_define_module_function
     *
     */
    public function  defineFastModuleFunction(name: String, method: Callback): void {
        defineFastPrivateMethod(name, method);
        getSingletonClass().defineFastMethod(name, method);
    }

    /** rb_define_module_function
     *
     */
    public function  defineFastPublicModuleFunction(name: String, method: Callback): void {
        defineFastMethod(name, method);
        getSingletonClass().defineFastMethod(name, method);
    }

    /** rb_alias
     *
     */
    public function  defineAlias(name: String, oldName: String): void {
        testFrozen("module");
        if (oldName == name) {
            return;
        }
        var runtime: Ruby = getRuntime();
        if (this == runtime.getObject()) {
            runtime.secure(4);
        }
        var method: DynamicMethod = searchMethod(oldName);
        var oldMethod: DynamicMethod = searchMethod(name);
        if (method.isUndefined()) {
            if (isModule()) {
                method = runtime.getObject().searchMethod(oldName);
            }

            if (method.isUndefined()) {
                throw runtime.newNameError("undefined method `" + oldName + "' for " +
                        (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
            }
        }
        var cacheMap: CacheMap = runtime.getCacheMap();
        cacheMap.remove(method);
        cacheMap.remove(oldMethod);
        if (oldMethod != oldMethod.getRealMethod()) {
            cacheMap.remove(oldMethod.getRealMethod());
        }
        putMethod(name, new AliasMethod(this, method, oldName));
    }

    public function  defineAliases(aliases: Array, oldName: String): void {
        testFrozen("module");
        var runtime: Ruby = getRuntime();
        if (this == runtime.getObject()) {
            runtime.secure(4);
        }
        var method: DynamicMethod = searchMethod(oldName);
        if (method.isUndefined()) {
            if (isModule()) {
                method = runtime.getObject().searchMethod(oldName);
            }

            if (method.isUndefined()) {
                throw runtime.newNameError("undefined method `" + oldName + "' for " +
                        (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
            }
        }
        var cacheMap: CacheMap = runtime.getCacheMap();
        cacheMap.remove(method);
        for (var name:String in aliases) {
            if (oldName == name) continue;
            var oldMethod: DynamicMethod = searchMethod(name);
            cacheMap.remove(oldMethod);
            if (oldMethod != oldMethod.getRealMethod()) {
                cacheMap.remove(oldMethod.getRealMethod());
            }
            putMethod(name, new AliasMethod(this, method, oldName));
        }
    }

    /** this method should be used only by interpreter or compiler 
     * 
     */
    public function  defineOrGetClassUnder(name: String, superClazz: RubyClass): RubyClass {
        // This method is intended only for defining new classes in Ruby code,
        // so it uses the allocator of the specified superclass or default to
        // the Object allocator. It should NOT be used to define classes that require a native allocator.

        var runtime: Ruby = getRuntime();
        var classObj: IRubyObject = getConstantAt(name);
        var clazz:RubyClass;

        if (classObj != null) {
            if (!(classObj instanceof RubyClass)) throw runtime.newTypeError(name + " is not a class");
            clazz = classObj;

            if (superClazz != null) {
                var tmp: RubyClass = clazz.getSuperClass();
                while (tmp != null && tmp.isIncluded()) tmp = tmp.getSuperClass(); // need to skip IncludedModuleWrappers
                if (tmp != null) tmp = tmp.getRealClass();
                if (tmp != superClazz) throw runtime.newTypeError("superclass mismatch for class " + name);
                // superClazz = null;
            }

            if (runtime.getSafeLevel() >= 4) throw runtime.newTypeError("extending class prohibited");
        } else if (classProviders != null && (clazz = searchProvidersForClass(name, superClazz)) != null) {
            // reopen a java class
        } else {
            if (superClazz == null) superClazz = runtime.getObject();
            clazz = RubyClass.newClass(runtime, superClazz, name, superClazz.getAllocator(), this, true);
        }
        return clazz;
    }

    /** this method should be used only by interpreter or compiler 
     * 
     */
    public function  defineOrGetModuleUnder(name: String): RubyModule {
        // This method is intended only for defining new modules in Ruby code
        var runtime: Ruby = getRuntime();
        var moduleObj: IRubyObject = getConstantAt(name);
        var module:RubyModule;
        if (moduleObj != null) {
            if (!moduleObj.isModule()) throw runtime.newTypeError(name + " is not a module");
            if (runtime.getSafeLevel() >= 4) throw runtime.newSecurityError("extending module prohibited");
            module = moduleObj;
        }  else if (classProviders != null && (module = searchProvidersForModule(name)) != null) {
            // reopen a java module
        } else {       
            module = RubyModule.newModule(runtime, name, this, true);
        }
        return module;
    }

    /** rb_define_class_under
     *  this method should be used only as an API to define/open nested classes 
     */
    public function  defineClassUnder(name: String, superClass: RubyClass, allocator: ObjectAllocator): RubyClass {
        return getRuntime().defineClassUnder(name, superClass, allocator, this);
    }

    /** rb_define_module_under
     *  this method should be used only as an API to define/open nested module
     */
    public function  defineModuleUnder(name: String): RubyModule {
        return getRuntime().defineModuleUnder(name, this);
    }

    // FIXME: create AttrReaderMethod, AttrWriterMethod, for faster attr access
    private function  addAccessor(context: ThreadContext, internedName: String, readable: Boolean, writeable: Boolean): void {
        //assert internedName == internedName.intern() : internedName + " is not interned";

         var runtime: Ruby = getRuntime();

        // Check the visibility of the previous frame, which will be the frame in which the class is being eval'ed
        var attributeScope: Visibility = context.getCurrentVisibility();
        if (attributeScope == Visibility.PRIVATE) {
            //FIXME warning
        } else if (attributeScope == Visibility.MODULE_FUNCTION) {
            attributeScope = Visibility.PRIVATE;
            // FIXME warning
        }
        var variableName:String = ("@" + internedName);
        if (readable) {
            // FIXME: should visibility be set to current visibility?
            addMethod(internedName, new JavaMethod(this, Visibility.PUBLIC) (
                function  call(context: ThreadContext, self: IRubyObject, clazz: RubyModule, name: String, args: Array, block: Block): IRubyObject {
                    if (args.length != 0) Arity.raiseArgumentError(runtime, args.length, 0, 0);

                    var variable: IRubyObject = self.getInstanceVariables().fastGetInstanceVariable(variableName);

                    return variable == null ? runtime.getNil() : variable;
                }
				,
                function  getArity(): Arity {
                    return Arity.noArguments();
                }
            ));
            callMethod2(context, "method_added", runtime.fastNewSymbol(internedName));
        }
        if (writeable) {
            internedName = (internedName + "=");
            // FIXME: should visibility be set to current visibility?
            addMethod(internedName, new JavaMethod(this, Visibility.PUBLIC) (
                function  call(context: ThreadContext, self: IRubyObject, clazz: RubyModule, name: String, args: Array, block: Block): IRubyObject {
                    // ENEBO: Can anyone get args to be anything but length 1?
                    if (args.length != 1) Arity.raiseArgumentError(runtime, args.length, 1, 1);

                    return self.getInstanceVariables().fastSetInstanceVariable(variableName, args[0]);
                }
				,
                function  getArity(): Arity {
                    return Arity.singleArgument();
                }
            ));
            callMethod2(context, "method_added", runtime.fastNewSymbol(internedName));
        }
    }

    /** set_method_visibility
     *
     */
    public function  setMethodVisibility(methods: Array, visibility: Visibility): void {
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError("Insecure: can't change method visibility");
        }

        for (var i: int = 0; i < methods.length; i++) {
            exportMethod(methods[i].asJavaString(), visibility);
        }
    }

    /** rb_export_method
     *
     */
    public function  exportMethod(name: String, visibility: Visibility): void {
        if (this == getRuntime().getObject()) {
            getRuntime().secure(4);
        }

        var method: DynamicMethod = searchMethod(name);

        if (method.isUndefined()) {
            throw getRuntime().newNameError("undefined method '" + name + "' for " +
                                (isModule() ? "module" : "class") + " '" + getName() + "'", name);
        }

        if (method.getVisibility() != visibility) {
            if (this == method.getImplementationClass()) {
                method.setVisibility(visibility);
            } else {
                // FIXME: Why was this using a FullFunctionCallbackMethod before that did callSuper?
                addMethod(name, new WrapperMethod(this, method, visibility));
            }
        }
    }

    /**
     * MRI: rb_method_boundp
     *
     */
    public function  isMethodBound(name: String, checkVisibility: Boolean): Boolean {
        var method: DynamicMethod = searchMethod(name);
        if (!method.isUndefined()) {
            return !(checkVisibility && method.getVisibility() == Visibility.PRIVATE);
        }
        return false;
    }

    public function  newMethod(receiver: IRubyObject, name: String, bound: Boolean): IRubyObject {
        var method: DynamicMethod = searchMethod(name);
        if (method.isUndefined()) {
            throw getRuntime().newNameError("undefined method `" + name +
                "' for class `" + this.getName() + "'", name);
        }

        var implementationModule: RubyModule = method.getImplementationClass();
        var originModule: RubyModule = this;
        while (originModule != implementationModule && originModule.isSingleton()) {
            originModule = originModule.getRealClass();
        }

        var newMethod: JRubyMethod = null;
        if (bound) {
            newMethod  = RubyMethod.newMethod(implementationModule, name, originModule, name, method, receiver);
        } else {
            newMethod  = RubyUnboundMethod.newUnboundMethod(implementationModule, name, originModule, name, method);
        }
        newMethod.infectBy(this);

        return newMethod;
    }

    // What is argument 1 for in this method? A Method or Proc object /OB
    //@JRubyMethod(name = "define_method", required = 1, optional = 1, frame = var visibility: true = Visibility.PRIVATE)
    public function  define_method(context: ThreadContext, args: Array, block: Block): IRubyObject {
        if (args.length < 1 || args.length > 2) {
            throw getRuntime().newArgumentError("wrong # of arguments(" + args.length + " for 1)");
        }

        var body:IRubyObject;
        var name: String = args[0].asJavaString();
        var newMethod:DynamicMethod = null;
        var visibility: Visibility = context.getCurrentVisibility();

        if (visibility == Visibility.MODULE_FUNCTION) visibility = Visibility.PRIVATE;
        if (args.length == 1) {
            // double-testing args.length here, but it avoids duplicating the proc-setup code in two places
            var proc: RubyProc = getRuntime().newProc(Block.Type.LAMBDA, block);
            body = proc;
            
            // a normal block passed to define_method changes to do arity checking; make it a lambda
            proc.getBlock().type = Block.Type.LAMBDA;

            newMethod  = createProcMethod(name, visibility, proc);
        } else if (args.length == 2) {
            if (getRuntime().getProc().isInstance(args[1])) {
                // double-testing args.length here, but it avoids duplicating the proc-setup code in two places
                var proc:RubyProc = RubyProc(args[1]);
                body = proc;

                newMethod  = createProcMethod(name, visibility, proc);
            } else if (getRuntime().getMethod().isInstance(args[1])) {
                var method:RubyMethod = RubyMethod(args[1]);
                body = method;

                newMethod  = new MethodMethod2(this, method.unbind(null), visibility);
            } else {
                throw getRuntime().newTypeError("wrong argument type " + args[1].getType().getName() + " (expected Proc/Method)");
            }
        } else {
            throw getRuntime().newArgumentError("wrong # of arguments(" + args.length + " for 1)");
        }

        addMethod(name, newMethod);

        var symbol: RubySymbol = getRuntime().fastNewSymbol(name);

        if (context.getPreviousVisibility() == Visibility.MODULE_FUNCTION) {
            getSingletonClass().addMethod(name, new WrapperMethod(getSingletonClass(), newMethod, Visibility.PUBLIC));
        }

        if(isSingleton()){
            var singleton:IRubyObject = this.getAttached(); 
            singleton.callMethod2(context, "singleton_method_added", symbol);
        }else{
            callMethod2(context, "method_added", symbol);
        }

        return body;
    }
    
/*     private function  createProcMethod(name: String, visibility: Visibility, proc: RubyProc): DynamicMethod {
        proc.getBlock().getBinding().getFrame().setKlazz(this);
        proc.getBlock().getBinding().getFrame().setName(name);

        // for zsupers in define_method (blech!) we tell the proc scope to act as the "argument" scope
        proc.getBlock().getBody().getStaticScope().setArgumentScope(true);
        // just using required is broken...but no more broken than before zsuper refactoring
        proc.getBlock().getBody().getStaticScope().setRequiredArgs(proc.getBlock().arity().required());

        return new ProcMethod(this, proc, visibility);
    } */

    public function  executeUnder(context: ThreadContext, method: Callback, args: Array, block: Block): IRubyObject {
        context.preExecuteUnder(this, block);
        var val:Object = method.execute(this, args, block);
        context.postExecuteUnder();
        return val;
    }

    //@JRubyMethod(name = "name")
    public function  name(): RubyString {
        return getRuntime().newString(getBaseName() == null ? "" : getName());
    }

    public function cloneMethods(clone:RubyModule):IRubyObject {
        var realType: RubyModule = this.getNonIncludedClass();
        for (var entry:Object in getMethods().entrySet()) {
            var method: DynamicMethod = entry.getValue();
            // Do not clone cached methods
            // FIXME: MRI copies all methods here
            if (method.getImplementationClass() == realType || method instanceof UndefinedMethod) {
                
                // A cloned method now beNumbers to a new class.  Set it.
                // TODO: Make DynamicMethod immutable
                var clonedMethod: DynamicMethod = method.dup();
                clonedMethod.setImplementationClass(clone);
                clone.putMethod(entry.getKey(), clonedMethod);
            }
        }

        return clone;
    }

    /** rb_mod_init_copy
     * 
     */
    //@JRubyMethod(name = var required: "initialize_copy" = 1)
    public override function  initialize_copy(original: IRubyObject): IRubyObject {
        super.initialize_copy(original);

        var originalModule:RubyModule = original;

        if (!getMetaClass().isSingleton()) setMetaClass(originalModule.getSingletonClassClone());
        setSuperClass(originalModule.getSuperClass());

        if (originalModule.hasVariables()){
            syncVariables(originalModule.getVariableArray());
        }

        originalModule.cloneMethods(this);

        return this;
    }

    /** rb_mod_included_modules
     *
     */
    //@JRubyMethod(name = "included_modules")
    public function  included_modules(): RubyArray {
        var ary: RubyArray = getRuntime().newArray();

        for (var p:RubyModule = getSuperClass(); p != null; p = p.getSuperClass()) {
            if (p.isIncluded()) {
                ary.append(p.getNonIncludedClass());
            }
        }

        return ary;
    }

    /** rb_mod_ancestors
     *
     */
    //@JRubyMethod(name = "ancestors")
    public function  ancestors(): RubyArray {
        var ary: RubyArray = RubyArray(getRuntime().newArray(getAncestorArray()));

        return ary;
    }

    public function  getAncestorArray(): Array {
        var list: Array = new Array();

        for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            if(!p.isSingleton()) {
                list.add(p.getNonIncludedClass());
            }
        }

        return list;
    }

    public function  hasModuleInHierarchy(type: RubyModule): Boolean {
        // XXX: This check previously used callMethod("==") to check for equality between classes
        // when scanning the hierarchy. However the == check may be safe; we should only ever have
        // one instance bound to a given type/constant. If it's found to be unsafe, examine ways
        // to avoid the == call.
  		for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            if (p.getNonIncludedClass() == type) return true;
        }

        return false;
    }

    public override function  hashCode(): int {
        return _id;
    }

    //@JRubyMethod(name = "hash")
    public override function  hash(): RubyFixnum {
        return getRuntime().newFixnum(_id);
    }

    /** rb_mod_to_s
     *
     */
    //@JRubyMethod(name = "to_s")
    public override function  to_s(): IRubyObject {
        if(isSingleton()){            
            var attached:IRubyObject = this.getAttached();
            var buffer: String = "#<Class:";
            if(attached instanceof RubyClass || attached instanceof RubyModule){
                buffer += attached.inspect();
            }else{
                buffer.concat(attached.anyToString());
            }
            buffer.concat(">");
            return getRuntime().newString(buffer.toString());
        }
        return getRuntime().newString(getName());
    }

    //rb_mod_eqq
    /*@JRubyMethod(name = "===var required: " = 1)
    public override function  op_eqq(obj: IRubyObject): RubyBoolean {
        return getRuntime().newBoolean(isInstance(obj));
    }*/

    //@JRubyMethod(name = "==var required: " = 1)
    public override function  op_equal(context: ThreadContext, other: IRubyObject): IRubyObject {
        return super.op_equal(context, other);
    }


    //@JRubyMethod(name = "freeze")
/*     public override function  freeze(): IRubyObject {
        to_s();
        return super.freeze();
    } */

    /** rb_mod_le
    *
    */
    //@JRubyMethod(name = "<=var required: " = 1)
   public function  op_le(obj: IRubyObject): IRubyObject {
        if (!(obj instanceof RubyModule)) {
            throw getRuntime().newTypeError("compared with non class/module");
        }

        if (isKindOfModule( obj)) {
            return getRuntime().getTrue();
        } else if (( obj).isKindOfModule(this)) {
            return getRuntime().getFalse();
        }

        return getRuntime().getNil();
    }

    /** rb_mod_lt
    *
    */
    //@JRubyMethod(name = var required: "<" = 1)
   public function  op_lt(obj: IRubyObject): IRubyObject {
        return obj == this ? getRuntime().getFalse() : op_le(obj);
    }

    /** rb_mod_ge
    *
    */
    //@JRubyMethod(name = ">=var required: " = 1)
   public function  op_ge(obj: IRubyObject): IRubyObject {
        if (!(obj instanceof RubyModule)) {
            throw getRuntime().newTypeError("compared with non class/module");
        }

        return RubyModule( obj).op_le(this);
    }

    /** rb_mod_gt
    *
    */
    //@JRubyMethod(name = var required: ">" = 1)
   public function  op_gt(obj: IRubyObject): IRubyObject {
        return this == obj ? getRuntime().getFalse() : op_ge(obj);
    }

    /** rb_mod_cmp
    *
    */
    //@JRubyMethod(name = "<=var required: >" = 1)
   public function  op_cmp(obj: IRubyObject): IRubyObject {
        if (this == obj) return getRuntime().newFixnum(0);
        if (!(obj instanceof RubyModule)) return getRuntime().getNil();

        var module:RubyModule = RubyModule( obj );

        if (module.isKindOfModule(this)) {
            return getRuntime().newFixnum(1);
        } else if (this.isKindOfModule(module)) {
            return getRuntime().newFixnum(-1);
        }

        return getRuntime().getNil();
    }

    public function  isKindOfModule(type: RubyModule): Boolean {
         for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            if (p.isSame(type)) {
                return true;
            }
        }

        return false;
    }

    protected function isSame( module:RubyModule):Boolean {
        return this == module;
    }

    /** rb_mod_initialize
     *
     */
    //@JRubyMethod(name = "initialize", frame = var visibility: true = Visibility.PRIVATE)
    public override function initialize(): IRubyObject {
       /*  if (block.isGiven()) {
            // class and module bodies default to public, so make the block's visibility public. JRUBY-1185.
            block.getBinding().setVisibility(Visibility.PUBLIC);
            ///block.yield(getRuntime().getCurrentContext(), null, this, this, false);
        } */

        return getRuntime().getNil();
    }

    /** rb_mod_attr
     *
     */
    //@JRubyMethod(name = "attr", required = 1, optional = var visibility: 1 = Visibility.PRIVATE)
    public function  attr(context: ThreadContext, args: Array): IRubyObject {
        var writeable: Boolean = args.length > 1 ? args[1].isTrue() : false;

        addAccessor(context, args[0].asJavaString(), true, writeable);

        return getRuntime().getNil();
    }

    /**
     * @deprecated
     */
    public function  attr_reader(args: Array): IRubyObject {
        return attr_reader2(getRuntime().getCurrentContext(), args);
    }
    
    /** rb_mod_attr_reader
     *
     */
    //@JRubyMethod(name = "attr_reader", rest = var visibility: true = Visibility.PRIVATE)
    public function  attr_reader2(context: ThreadContext, args: Array): IRubyObject {
        for (var i: int = 0; i < args.length; i++) {
            addAccessor(context, args[i].asJavaString(), true, false);
        }

        return getRuntime().getNil();
    }

    /** rb_mod_attr_writer
     *
     */
    //@JRubyMethod(name = "attr_writer", rest = var visibility: true = Visibility.PRIVATE)
    public function  attr_writer(context: ThreadContext, args: Array): IRubyObject {
        for (var i: int = 0; i < args.length; i++) {
            addAccessor(context, args[i].asJavaString(), false, true);
        }

        return getRuntime().getNil();
    }

    /**
     * @deprecated
     */
    public function  attr_accessor(args: Array): IRubyObject {
        return attr_accessor(getRuntime().getCurrentContext(), args);
    }

    /** rb_mod_attr_accessor
     *
     */
    //@JRubyMethod(name = "attr_accessor", rest = var visibility: true = Visibility.PRIVATE)
    public function  attr_accessor2(context: ThreadContext, args: Array): IRubyObject {
        for (var i: int = 0; i < args.length; i++) {
            // This is almost always already interned, since it will be called with a symbol in most cases
            // but when created from Java code, we might get an argument that needs to be interned.
            // addAccessor has as a precondition that the string MUST be interned
            addAccessor(context, args[i].asJavaString(), true, true);
        }

        return getRuntime().getNil();
    }

    /**
     * Get a list of all instance methods names of the provided visibility unless not is true, then 
     * get all methods which are not the provided visibility.
     * 
     * @param args passed into one of the Ruby instance_method methods
     * @param visibility to find matching instance methods against
     * @param not if true only find methods not matching supplied visibility
     * @return a RubyArray of instance method names
     */
    private function  instance_methods(args: Array, visibility: Visibility,  not:Boolean): RubyArray {
        var includeSuper: Boolean = args.length > 0 ? args[0].isTrue() : true;
        var ary: RubyArray = getRuntime().newArray();
        var seen: Dictionary = new Dictionary();

        for (var type:RubyModule = this; type != null; type = type.getSuperClass()) {
            var realType: RubyModule = type.getNonIncludedClass();
            for (var methodName: String in type.getMethods()) {               
                var method:DynamicMethod = type.getMethods()[methodName];
               
                if (! seen.contains(methodName)) {
                    seen.add(methodName);
                    
                    if (method.getImplementationClass() == realType &&
                        (!not && method.getVisibility() == visibility || (not && method.getVisibility() != visibility)) &&
                        ! method.isUndefined()) {

                        ary.append(getRuntime().newString(methodName));
                    }
                }
            }

            if (!includeSuper) {
                break;
            }
        }

        return ary;
    }

    //@JRubyMethod(name = var optional: "instance_methods" = 1)
    public function  instance_methods2(args: Array): RubyArray {
        return instance_methods(args, Visibility.PRIVATE, true);
    }

    //@JRubyMethod(name = var optional: "public_instance_methods" = 1)
    public function  public_instance_methods(args: Array): RubyArray {
        return instance_methods(args, Visibility.PUBLIC, false);
    }

    //@JRubyMethod(name = var required: "instance_method" = 1)
    public function  instance_method(symbol: IRubyObject): IRubyObject {
        return newMethod(null, symbol.asJavaString(), false);
    }

    /** rb_class_protected_instance_methods
     *
     */
    //@JRubyMethod(name = var optional: "protected_instance_methods" = 1)
    public function  protected_instance_methods(args: Array): RubyArray {
        return instance_methods(args, Visibility.PROTECTED, false);
    }

    /** rb_class_private_instance_methods
     *
     */
    //@JRubyMethod(name = var optional: "private_instance_methods" = 1)
    public function  private_instance_methods(args: Array): RubyArray {
        return instance_methods(args, Visibility.PRIVATE, false);
    }

    /** rb_mod_append_features
     *
     */
    //@JRubyMethod(name = "append_features", required = var visibility: 1 = Visibility.PRIVATE)
    public function  append_features(module: IRubyObject): RubyModule {
        if (!(module instanceof RubyModule)) {
            // MRI error message says Class, even though Module is ok 
            throw getRuntime().newTypeError(module,getRuntime().getClassClass());
        }
        RubyModule( module).includeModule(this);
        return this;
    }

    /** rb_mod_extend_object
     *
     */
    //@JRubyMethod(name = "extend_object", required = var visibility: 1 = Visibility.PRIVATE)
    public function  extend_object(obj: IRubyObject): IRubyObject {
        obj.getSingletonClass().includeModule(this);
        return obj;
    }

    /** rb_mod_include
     *
     */
    //@JRubyMethod(name = "include", required = 1, rest = var visibility: true = Visibility.PRIVATE)
    public function include2(modules: Array): RubyModule {
        var context: ThreadContext = getRuntime().getCurrentContext();
        // MRI checks all types first:
        for (var i:int = modules.length; --i >= 0; ) {
            var obj: IRubyObject = modules[i];
            if (!obj.isModule()) throw getRuntime().newTypeError(obj,getRuntime().getModule());
        }
         for (var i:int = modules.length - 1; i >= 0; i--) {
            modules[i].callMethod(context, "append_features", this);
            modules[i].callMethod(context, "included", this);
        }

        return this;
    }

    //@JRubyMethod(name = var required: "included" = 1)
    public function  included(other: IRubyObject): IRubyObject {
        return getRuntime().getNil();
    }

    //@JRubyMethod(name = "extended", required = var frame: 1 = true)
    public function  extended(other: IRubyObject, block: Block): IRubyObject {
        return getRuntime().getNil();
    }

    private function  setVisibility(context: ThreadContext, args: Array, visibility: Visibility): void {
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError("Insecure: can't change method visibility");
        }

        if (args.length == 0) {
            // Note: we change current frames visibility here because the methods which call
            // this method are all "fast" (e.g. they do not created their own frame).
            context.setCurrentVisibility(visibility);
        } else {
            setMethodVisibility(args, visibility);
        }
    }

    /** rb_mod_public
     *
     */
    //@JRubyMethod(name = "public", rest = var visibility: true = Visibility.PRIVATE)
    public function  rbPublic(context: ThreadContext, args: Array): RubyModule {
        setVisibility(context, args, Visibility.PUBLIC);
        return this;
    }

    /** rb_mod_protected
     *
     */
    //@JRubyMethod(name = "protected", rest = var visibility: true = Visibility.PRIVATE)
    public function  rbProtected(context: ThreadContext, args: Array): RubyModule {
        setVisibility(context, args, Visibility.PROTECTED);
        return this;
    }

    /** rb_mod_private
     *
     */
    //@JRubyMethod(name = "private", rest = var visibility: true = Visibility.PRIVATE)
    public function  rbPrivate(context: ThreadContext, args: Array): RubyModule {
        setVisibility(context, args, Visibility.PRIVATE);
        return this;
    }

    /** rb_mod_modfunc
     *
     */
    //@JRubyMethod(name = "module_function", rest = var visibility: true = Visibility.PRIVATE)
    public function  module_function(args: Array): RubyModule {
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError("Insecure: can't change method visibility");
        }

        var context: ThreadContext = getRuntime().getCurrentContext();

        if (args.length == 0) {
            context.setCurrentVisibility(Visibility.MODULE_FUNCTION);
        } else {
            setMethodVisibility(args, Visibility.PRIVATE);

            for (var i: int = 0; i < args.length; i++) {
                var name: String = args[i].asJavaString();
                var method: DynamicMethod = searchMethod(name);
                //assert !method.isUndefined() : "undefined method '" + name + "'";
                getSingletonClass().addMethod(name, new WrapperMethod(getSingletonClass(), method, Visibility.PUBLIC));
                callMethod(context, "singleton_method_added", getRuntime().fastNewSymbol(name));
            }
        }
        return this;
    }

    //@JRubyMethod(name = "method_added", required = var visibility: 1 = Visibility.PRIVATE)
    public function  method_added(nothing: IRubyObject): IRubyObject {
        return getRuntime().getNil();
    }

    //@JRubyMethod(name = "method_removed", required = var visibility: 1 = Visibility.PRIVATE)
    public function  method_removed(nothing: IRubyObject): IRubyObject {
        return getRuntime().getNil();
    }

    //@JRubyMethod(name = "method_undefined", required = var visibility: 1 = Visibility.PRIVATE)
    public function  method_undefined(nothing: IRubyObject): IRubyObject {
        return getRuntime().getNil();
    }
    
    //@JRubyMethod(name = var required: "method_defined?" = 1)
    public function  method_defined_p(symbol: IRubyObject): RubyBoolean {
        return isMethodBound(symbol.asJavaString(), true) ? getRuntime().getTrue() : getRuntime().getFalse();
    }

    //@JRubyMethod(name = var required: "public_method_defined?" = 1)
    public function  public_method_defined(symbol: IRubyObject): IRubyObject {
	    var method: DynamicMethod = searchMethod(symbol.asJavaString());
	    
		return getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == Visibility.PUBLIC);
    }

    //@JRubyMethod(name = var required: "protected_method_defined?" = 1)
    public function  protected_method_defined(symbol: IRubyObject): IRubyObject {
	    var method: DynamicMethod = searchMethod(symbol.asJavaString());
	    
		return getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == Visibility.PROTECTED);
    }
	
    //@JRubyMethod(name = var required: "private_method_defined?" = 1)
    public function  private_method_defined(symbol: IRubyObject): IRubyObject {
	    var method: DynamicMethod = searchMethod(symbol.asJavaString());
	    
		return getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == Visibility.PRIVATE);
    }

    //@JRubyMethod(name = var rest: "public_class_method" = true)
    public function  public_class_method(args: Array): RubyModule {
        getMetaClass().setMethodVisibility(args, Visibility.PUBLIC);
        return this;
    }

    //@JRubyMethod(name = var rest: "private_class_method" = true)
    public function  private_class_method(args: Array): RubyModule {
        getMetaClass().setMethodVisibility(args, Visibility.PRIVATE);
        return this;
    }

    //@JRubyMethod(name = "alias_method", required = var visibility: 2 = Visibility.PRIVATE)
    public function  alias_method(context: ThreadContext, newId: IRubyObject, oldId: IRubyObject): RubyModule {
        var newName: String = newId.asJavaString();
        defineAlias(newName, oldId.asJavaString());
        var newSym: RubySymbol = newId instanceof RubySymbol ? RubySymbol(newId) :
            context.getRuntime().newSymbol(newName);
        if (isSingleton()) {
            MetaClass(this).getAttached().callMethod2(context, "singleton_method_added", newSym);
        } else {
            callMethod2(context, "method_added", newSym);
        }
        return this;
    }

    //@JRubyMethod(name = "undef_method", required = 1, rest = var visibility: true = Visibility.PRIVATE)
    public function  undef_method(context: ThreadContext, args: Array): RubyModule {
        for (var i: int=0; i<args.length; i++) {
            undef(context, args[i].asJavaString());
        }
        return this;
    }

    //@JRubyMethod(name = {"module_eval", "class_eval"}, optional = var frame: 3 = true)
    public function  module_eval(context: ThreadContext, args: Array, block: Block): IRubyObject {
        return specificEval(context, this, args, block);
    }

    //@JRubyMethod(name = "remove_method", required = 1, rest = var visibility: true = Visibility.PRIVATE)
    public function  remove_method(context: ThreadContext, args: Array): RubyModule {
        for(var i: int=0;i<args.length;i++) {
            removeMethod(context, args[i].asJavaString());
        }
        return this;
    }

/*     public static function  marshalTo(module: RubyModule, output: MarshalStream): void  {
        output.registerLinkTarget(module);
        output.writeString(MarshalStream.getPathFromClass(module));
    }

    public static function  unmarshalFrom(input: UnmarshalStream): RubyModule {
        var name: String = RubyString.byteArrayToString(input.unmarshalString());
        var result: RubyModule = UnmarshalStream.getModuleFromPath(input.getRuntime(), name);
        input.registerLinkTarget(result);
        return result;
    } */

    /* Module class methods */
    
    /** 
     * Return an array of nested modules or classes.
     */
    //@JRubyMethod(name = "nesting", frame = var meta: true = true)
    public static function  nesting(context: ThreadContext, recv: IRubyObject, block: Block): RubyArray {
        var runtime: Ruby = recv.getRuntime();
        var object: RubyModule = runtime.getObject();
        var scope: StaticScope = context.getCurrentScope().getStaticScope();
        var result: RubyArray = runtime.newArray();
        
        for(var current:StaticScope = scope; current.getModule() != object; current = current.getPreviousCRefScope()) {
            result.append(current.getModule());
        }
        
        return result;
    }

    private function  doIncludeModule(includedModule: RubyModule): void {
        var skip: Boolean = false;

        var currentModule: RubyModule = this;
        while (includedModule != null) {

            if (getNonIncludedClass() == includedModule.getNonIncludedClass()) {
                throw getRuntime().newArgumentError("cyclic include detected");
            }

            var superclassSeen: Boolean = false;

            // scan class hierarchy for module
            for (var superClass:RubyModule = this.getSuperClass(); superClass != null; superClass = superClass.getSuperClass()) {
                if (superClass instanceof IncludedModuleWrapper) {
                    if (superClass.getNonIncludedClass() == includedModule.getNonIncludedClass()) {
                        if (!superclassSeen) {
                           	currentModule = superClass;
                        }
                        skip = true;
                        break;
                    }
                } else {
                   	superclassSeen = true;
                }
            }

            if (!skip) {

                // blow away caches for any methods that are redefined by module
                getRuntime().getCacheMap().moduleIncluded(currentModule, includedModule);
                
                // In the current logic, if we get here we know that module is not an
                // IncludedModuleWrapper, so there's no need to fish out the delegate. But just
                // in case the logic should change later, let's do it anyway:
                currentModule.setSuperClass(new IncludedModuleWrapper(getRuntime(), currentModule.getSuperClass(),
                        includedModule.getNonIncludedClass()));
                currentModule = currentModule.getSuperClass();
            }

            includedModule = includedModule.getSuperClass();
            skip = false;
        }
    }


    //
    ////////////////// CLASS VARIABLE RUBY METHODS ////////////////
    //

    //@JRubyMethod(name = var required: "class_variable_defined?" = 1)
    public function  class_variable_defined_p(vars: IRubyObject): IRubyObject {
        var internedName: String = validateClassVariable(vars.asJavaString());
        var module: RubyModule = this;
        do {
            if (module.fastHasClassVariable(internedName)) {
                return getRuntime().getTrue();
            }
        } while ((module = module.getSuperClass) != null);

        return getRuntime().getFalse();
    }

    /** rb_mod_cvar_get
     *
     */
    //@JRubyMethod(name = "class_variable_get", required = var visibility: 1 = Visibility.PRIVATE)
    public function  class_variable_get(vars: IRubyObject): IRubyObject {
        return fastGetClassVar(validateClassVariable(vars.asJavaString()));
    }

    /** rb_mod_cvar_set
     *
     */
    //@JRubyMethod(name = "class_variable_set", required = var visibility: 2 = Visibility.PRIVATE)
    public function  class_variable_set(vars: IRubyObject, value: IRubyObject): IRubyObject {
        return fastSetClassVar(validateClassVariable(vars.asJavaString()), value);
    }

    /** rb_mod_remove_cvar
     *
     */
    //@JRubyMethod(name = "remove_class_variable", required = var visibility: 1 = Visibility.PRIVATE)
    public function  remove_class_variable(name: IRubyObject): IRubyObject {
        var javaName: String = validateClassVariable(name.asJavaString());
        var value:IRubyObject;

        if ((value = deleteClassVariable(javaName)) != null) {
            return value;
        }

        if (fastIsClassVarDefined(javaName)) {
            throw cannotRemoveError(javaName);
        }

        throw getRuntime().newNameError("class variable " + javaName + " not defined for " + getName(), javaName);
    }

    /** rb_mod_class_variables
     *
     */
    //@JRubyMethod(name = "class_variables")
    public function  class_variables(): RubyArray {
        var names: Dictionary = new Dictionary();

        for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            for (var name :String in p.getClassVariableNameArray()) {
                names.add(name);
            }
        }

        var runtime: Ruby = getRuntime();
        var ary: RubyArray = runtime.newArray();

        for (var name : String in names) {
            ary.add(runtime.newString(name));
        }

        return ary;
    }


    //
    ////////////////// CONSTANT RUBY METHODS ////////////////
    //

    /** rb_mod_const_defined
     *
     */
    //@JRubyMethod(name = var required: "const_defined?" = 1)
    public function  const_defined_p(symbol: IRubyObject): RubyBoolean {
        // Note: includes part of fix for JRUBY-1339
        return getRuntime().newBoolean(fastIsConstantDefined(validateConstant(symbol.asJavaString())));
    }

    /** rb_mod_const_get
     *
     */
    //@JRubyMethod(name = var required: "const_get" = 1)
    public function  const_get(symbol: IRubyObject): IRubyObject {
        return fastGetConstant(validateConstant(symbol.asJavaString()));
    }

    /** rb_mod_const_set
     *
     */
    //@JRubyMethod(name = var required: "const_set" = 2)
    public function  const_set(symbol: IRubyObject, value: IRubyObject): IRubyObject {
        return fastSetConstant(validateConstant(symbol.asJavaString()), value);
    }

    //@JRubyMethod(name = "remove_const", required = var visibility: 1 = Visibility.PRIVATE)
    public function  remove_const(name: IRubyObject): IRubyObject {
        var id: String = validateConstant(name.asJavaString());
        var value:IRubyObject;
        if ((value = deleteConstant(id)) != null) {
            if (value != getRuntime().getUndef()) {
                return value;
            }
            getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + id);
            // FIXME: I'm not sure this is right, but the old code returned
            // the undef, which definitely isn't right...
            return getRuntime().getNil();
        }

        if (hasConstantInHierarchy(id)) {
            throw cannotRemoveError(id);
        }

        throw getRuntime().newNameError("constant " + id + " not defined for " + getName(), id);
    }

    private function  hasConstantInHierarchy(name:String): Boolean {
        for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
            if (p.hasConstant(name)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Base implementation of Module#const_missing, throws NameError for specific missing constant.
     * 
     * @param name The constant name which was found to be missing
     * @return Nothing! Absolutely nothing! (though subclasses might choose to return something)
     */
    //@JRubyMethod(name = "const_missing", required = var frame: 1 = true)
    public function  const_missing(name: IRubyObject, block: Block): IRubyObject {
        /* Uninitialized constant */
        if (this != getRuntime().getObject()) {
            throw getRuntime().newNameError("uninitialized constant " + getName() + "::" + name.asJavaString(), "" + getName() + "::" + name.asJavaString());
        }

        throw getRuntime().newNameError("uninitialized constant " + name.asJavaString(), name.asJavaString());
    }

    /** rb_mod_constants
     *
     */
    //@JRubyMethod(name = "constants")
    public function  constants(): RubyArray {
        var runtime: Ruby = getRuntime();
        var array: RubyArray = runtime.newArray();
        var objectClass: RubyModule = runtime.getObject();

        if (getRuntime().getModule() == this) {

            for (var name :String in objectClass.getStoredConstantNameArray()) {
                array.add(runtime.newString(name));
            }

        } else if (objectClass == this) {

            for (var name :String in getStoredConstantNameArray()) {
                array.add(runtime.newString(name));
            }

        } else {
            var names: Dictionary = new Dictionary();
             for (var p:RubyModule = this; p != null; p = p.getSuperClass()) {
                if (objectClass != p) {
                    for (var name :String in p.getStoredConstantNameArray()) {
                        names.add(name);
                    }
                }
            }
            for (var name :String in names) {
                array.add(runtime.newString(name));
            }
        }

        return array;
    }


    //
    ////////////////// CLASS VARIABLE API METHODS ////////////////
    //

    /**
     * Set the named class variable to the given value, provided taint and freeze allow setting it.
     * 
     * Ruby C equivalent = "rb_cvar_set"
     * 
     * @param name The variable name to set
     * @param value The value to set it to
     */
    public function  setClassVar(name: String, value: IRubyObject): IRubyObject {
        var module: RubyModule = this;
        do {
            if (module.hasClassVariable(name)) {
                return module.storeClassVariable(name, value);
            }
        }  while ((module = module.getSuperClass()) != null);
        
        return storeClassVariable(name, value);
    }

    public function  fastSetClassVar(internedName:String, value: IRubyObject): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        var module: RubyModule = this;
        do {
            if (module.fastHasClassVariable(internedName)) {
                return module.fastStoreClassVariable(internedName, value);
            }
        } while ((module = module.getSuperClass()) != null);
        
        return fastStoreClassVariable(internedName, value);
    }

    /**
     * Retrieve the specified class variable, searching through this module, included modules, and supermodules.
     * 
     * Ruby C equivalent = "rb_cvar_get"
     * 
     * @param name The name of the variable to retrieve
     * @return The variable's value, or throws NameError if not found
     */
    public function  getClassVar(name: String): IRubyObject {
        //assert IdUtil.isClassVariable(name);
        var value:IRubyObject;
        var module: RubyModule = this;
        
        do {
            if ((value = module.variableTableFetch(name)) != null) return value;
        } while ((module = module.getSuperClass()) != null);

        throw getRuntime().newNameError("uninitialized class variable " + name + " in " + getName(), name);
    }

    public function  fastGetClassVar(internedName: String): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        //assert IdUtil.isClassVariable(internedName);
        var value:IRubyObject;
        var module: RubyModule = this;
        
        do {
        	if ((value = module.variableTableFastFetch(internedName)) != null) return value; 
        } while ((module = module.getSuperClass()) != null);

        throw getRuntime().newNameError("uninitialized class variable " + internedName + " in " + getName(), internedName);
    }

    /**
     * Is class var defined?
     * 
     * Ruby C equivalent = "rb_cvar_defined"
     * 
     * @param name The class var to determine "is defined?"
     * @return true if true, false if false
     */
    public function  isClassVarDefined(name: String): Boolean {
        var module: RubyModule = this;
        do {
            if (module.hasClassVariable(name)) return true;
        }  while ((module = module.getSuperClass()) != null);

        return false;
    }

    public function  fastIsClassVarDefined(internedName: String): Boolean {
        //assert internedName == internedName : internedName + " is not interned";
        var module: RubyModule = this;
        do {
            if (module.fastHasClassVariable(internedName)) return true;
        }  while ((module = module.getSuperClass()) != null);

        return false;
    }

    
    /** rb_mod_remove_cvar
     *
     * FIXME: any good reason to have two identical methods? (same as remove_class_variable)
     */
    public function  removeCvar(name: IRubyObject): IRubyObject { // Wrong Parameter ?
        var internedName: String = validateClassVariable(name.asJavaString());
        var value:IRubyObject;

        if ((value = deleteClassVariable(internedName)) != null) {
            return value;
        }

        if (fastIsClassVarDefined(internedName)) {
            throw cannotRemoveError(internedName);
        }

        throw getRuntime().newNameError("class variable " + internedName + " not defined for " + getName(), internedName);
    }


    //
    ////////////////// CONSTANT API METHODS ////////////////
    //

    public function  getConstantAt(name: String): IRubyObject {
        var value:IRubyObject;
        if ((value = fetchConstant(name)) != getRuntime().getUndef()) {
            return value;
        }
        deleteConstant(name);
        return getRuntime().getLoadService().autoload(getName() + "::" + name);
    }

    public function  fastGetConstantAt(internedName: String): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        var value:IRubyObject;
        if ((value = fastFetchConstant(internedName)) != getRuntime().getUndef()) {
            return value;
        }
        deleteConstant(internedName);
        return getRuntime().getLoadService().autoload(getName() + "::" + internedName);
    }

    /**
     * Retrieve the named constant, invoking 'const_missing' should that be appropriate.
     * 
     * @param name The constant to retrieve
     * @return The value for the constant, or null if not found
     */
    public function  getConstant(name: String): IRubyObject {
        //assert IdUtil.isConstant(name);
        var undef: IRubyObject = getRuntime().getUndef();
        var retryForModule: Boolean = false;
        var value:IRubyObject;
        var p: RubyModule = this;

        retry: while (true) {
            while (p != null) {
                if ((value = p.constantTableFetch(name)) != null) {
                    if (value != undef) {
                        return value;
                    }
                    p.deleteConstant(name);
                    if (getRuntime().getLoadService().autoload(
                            p.getName() + "::" + name) == null) {
                        break;
                    }
                    continue;
                }
                p = p.getSuperClass();
            };

            if (!retryForModule && !isClass()) {
                retryForModule = true;
                p = getRuntime().getObject();
                continue retry;
            }

            break;
        }

        return callMethod(getRuntime().getCurrentContext(),
                "const_missing", getRuntime().newSymbol(name));
    }
    
    public function  fastGetConstant(internedName: String): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        //assert IdUtil.isConstant(internedName);
        var undef: IRubyObject = getRuntime().getUndef();
        var retryForModule: Boolean = false;
        var value:IRubyObject;
        var p: RubyModule = this;

        retry: while (true) {
            while (p != null) {
                if ((value = p.constantTableFastFetch(internedName)) != null) {
                    if (value != undef) {
                        return value;
                    }
                    p.deleteConstant(internedName);
                    if (getRuntime().getLoadService().autoload(
                            p.getName() + "::" + internedName) == null) {
                        break;
                    }
                    continue;
                }
                p = p.getSuperClass();
            };

            if (!retryForModule && !isClass()) {
                retryForModule = true;
                p = getRuntime().getObject();
                continue retry;
            }

            break;
        }

        return callMethod(getRuntime().getCurrentContext(),
                "const_missing", getRuntime().fastNewSymbol(internedName));
    }

    // not actually called anywhere (all known uses call the fast version)
    public function  getConstantFrom(name: String): IRubyObject {
        return fastGetConstantFrom(name);
    }
    
    public function  fastGetConstantFrom(internedName: String): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        //assert IdUtil.isConstant(internedName);
        var objectClass: RubyClass = getRuntime().getObject();
        var undef: IRubyObject = getRuntime().getUndef();
        var value:IRubyObject;

        var p: RubyModule = this;
        
        while (p != null) {
            if ((value = p.constantTableFastFetch(internedName)) != null) {
                if (value != undef) {
                    if (p == objectClass && this != objectClass) {
                        var badCName: String = getName() + "::" + internedName;
                        getRuntime().getWarnings().warn(RubyWarnings.CONSTANT_BAD_REFERENCE, "toplevel constant " + 
                                internedName + " referenced by " + badCName, badCName);
                    }
                    return value;
                }
                p.deleteConstant(internedName);
                if (getRuntime().getLoadService().autoload(
                        p.getName() + "::" + internedName) == null) {
                    break;
                }
                continue;
            }
            p = p.getSuperClass();
        };

        return callMethod(getRuntime().getCurrentContext(),
                "const_missing", getRuntime().fastNewSymbol(internedName));
    }
    /**
     * Set the named constant on this module. Also, if the value provided is another Module and
     * that module has not yet been named, assign it the specified name.
     * 
     * @param name The name to assign
     * @param value The value to assign to it; if an unnamed Module, also set its basename to name
     * @return The result of setting the variable.
     */
    public function  setConstant(name: String, value: IRubyObject): IRubyObject {
        var oldValue:IRubyObject;
        if ((oldValue = fetchConstant(name)) != null) {
            if (oldValue == getRuntime().getUndef()) {
                getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + name);
            } else {
                getRuntime().getWarnings().warn(RubyWarnings.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + name, name);
            }
        }

        storeConstant(name, value);

        // if adding a module under a constant name, set that module's basename to the constant name
        if (value instanceof RubyModule) {
            var module:RubyModule = RubyModule(value);
            if (module.getBaseName() == null) {
                module.setBaseName(name);
                module.setParent(this);
            }
            /*
            module.setParent(this);
            */
        }
        return value;
    }

    public function  fastSetConstant(internedName: String, value: IRubyObject): IRubyObject {
        //assert internedName == internedName : internedName + " is not interned";
        var oldValue:IRubyObject;
        if ((oldValue = fastFetchConstant(internedName)) != null) {
            if (oldValue == getRuntime().getUndef()) {
                getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + internedName);
            } else {
                getRuntime().getWarnings().warn(RubyWarnings.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + internedName, internedName);
            }
        }

        fastStoreConstant(internedName, value);

        // if adding a module under a constant name, set that module's basename to the constant name
        if (value instanceof RubyModule) {
            var module:RubyModule = RubyModule(value);
            if (module.getBaseName() == null) {
                module.setBaseName(internedName);
                module.setParent(this);
            }
            /*
            module.setParent(this);
            */
        }
        return value;
    }
    
    /** rb_define_const
     *
     */
    public function  defineConstant(name: String, value: IRubyObject): void {
        //assert value != null;

        if (this == getRuntime().getClassClass()) {
            getRuntime().secure(4);
        }

        if (!IdUtil.isValidConstantName(name)) {
            throw getRuntime().newNameError("bad constant name " + name, name);
        }

        setConstant(name, value);
    }

    // Fix for JRUBY-1339 - search hierarchy for constant
    /** rb_const_defined_at
     * 
     */
    public function  isConstantDefined(name: String): Boolean {
        //assert IdUtil.isConstant(name);
        var isObject: Boolean = this == getRuntime().getObject();
        var undef: Object = getRuntime().getUndef();

        var module: RubyModule = this;

        do {
            var value:Object;
            if ((value = module.constantTableFetch(name)) != null) {
                if (value != undef) return true;
                return getRuntime().getLoadService().autoloadFor(
                        module.getName() + "::" + name) != null;
            }

        } while (isObject && (module = module.getSuperClass()) != null );

        return false;
    }

    public function  fastIsConstantDefined(internedName: String): Boolean {
        //assert internedName == internedName : internedName + " is not interned";
        //assert IdUtil.isConstant(internedName);
        var isObject: Boolean = this == getRuntime().getObject();
        var undef: Object = getRuntime().getUndef();

        var module: RubyModule = this;

        do {
            var value:Object;
            if ((value = module.constantTableFastFetch(internedName)) != null) {
                if (value != undef) return true;
                return getRuntime().getLoadService().autoloadFor(
                        module.getName() + "::" + internedName) != null;
            }

        } while (isObject && (module = module.getSuperClass()) != null );

        return false;
    }

    //
    ////////////////// COMMON CONSTANT / CVAR METHODS ////////////////
    //

/*     private function  cannotRemoveError(id: String): RaiseException {
        return getRuntime().newNameError("cannot remove " + id + " for " + getName(), id);
    } */


    //
    ////////////////// INTERNAL MODULE VARIABLE API METHODS ////////////////
    //
    
    /**
     * Behaves similarly to {@link #getClassVar(String)}. Searches this
     * class/module <em>and its ancestors</em> for the specified internal
     * variable.
     * 
     * @param name the internal variable name
     * @return the value of the specified internal variable if found, else null
     * @see #setInternalModuleVariable(String, IRubyObject)
     */
    public function  hasInternalModuleVariable(name:String): Boolean {
        var module: RubyModule = this;
        do {
            if (module.hasInternalVariable(name)) {
                return true;
            }
        } while ((module = module.getSuperClass()) != null);

        return false;
    }
    /**
     * Behaves similarly to {@link #getClassVar(String)}. Searches this
     * class/module <em>and its ancestors</em> for the specified internal
     * variable.
     * 
     * @param name the internal variable name
     * @return the value of the specified internal variable if found, else null
     * @see #setInternalModuleVariable(String, IRubyObject)
     */
    public function  searchInternalModuleVariable(name:String): IRubyObject {
        var module: RubyModule = this;
        var value:IRubyObject;
        do {
 			if ((value = module.getInternalVariable(name)) != null) {
                return value;
            }
        } while ((module = module.getSuperClass()) != null);

        return null;
    }

    /**
     * Behaves similarly to {@link #setClassVar(String, IRubyObject)}. If the
     * specified internal variable is found in this class/module <em>or an ancestor</em>,
     * it is set where found.  Otherwise it is set in this module. 
     * 
     * @param name the internal variable name
     * @param value the internal variable value
     * @see #searchInternalModuleVariable(String)
     */
    public function  setInternalModuleVariable(name:String,  value: IRubyObject): void {
        var module: RubyModule = this;
        do {
            if (module.hasInternalVariable(name)) {
                module.setInternalVariable(name, value);
                return;
            }
        }  while ((module = module.getSuperClass()) != null);

        setInternalVariable(name, value);
    }

    //
    ////////////////// LOW-LEVEL CLASS VARIABLE INTERFACE ////////////////
    //
    // fetch/store/list class variables for this module
    //
    
    public function  hasClassVariable(name: String): Boolean {
        //assert IdUtil.isClassVariable(name);
        return variableTableContains(name);
    }

    public function  fastHasClassVariable(internedName: String): Boolean {
        //assert IdUtil.isClassVariable(internedName);
        return variableTableFastContains(internedName);
    }

    public function  fetchClassVariable(name: String): IRubyObject {
        //assert IdUtil.isClassVariable(name);
        return variableTableFetch(name);
    }

    public function  fastFetchClassVariable(internedName: String): IRubyObject {
        //assert IdUtil.isClassVariable(internedName);
        return variableTableFastFetch(internedName);
    }

    public function  storeClassVariable(name: String, value: IRubyObject): IRubyObject {
        //assert IdUtil.isClassVariable(name) && value != null;
        ensureClassVariablesSettable();
        return variableTableStore(name, value);
    }

    public function  fastStoreClassVariable(internedName: String, value: IRubyObject): IRubyObject {
        //assert IdUtil.isClassVariable(internedName) && value != null;
        ensureClassVariablesSettable();
        return variableTableFastStore(internedName, value);
    }

    public function  deleteClassVariable(name: String): IRubyObject {
        //assert IdUtil.isClassVariable(name);
        ensureClassVariablesSettable();
        return variableTableRemove(name);
    }

    public function  getClassVariableList(): Array {
        var list: Array = new Array();
        var table: Array = variableTableGetTable();
        var readValue:IRubyObject;
 		for (var i:int = table.length; --i >= 0; ) {
            for (var e:VariableTableEntry = table[i]; e != null; e = e.next) {
                if (IdUtil.isClassVariable(e.name)) {
                    if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
                    list.add(new VariableEntry<IRubyObject>(e.name, readValue));
                }
            }
        }
        return list;
    }

    public function  getClassVariableNameList(): Array{
        var list: Array = new Array();
        var table: Array = variableTableGetTable();
         for (var i:int = table.length; --i >= 0; ) {
            for (var e:VariableTableEntry = table[i]; e != null; e = e.next) {
                if (IdUtil.isClassVariable(e.name)) {
                    list.add(e.name);
                }
            }
        }
        return list;
    }

    protected static  var ERR_INSECURE_SET_CLASS_VAR:String = "Insecure: can't modify class variable";
    protected static  var ERR_FROZEN_CVAR_TYPE:String = "class/module ";
   
    protected function validateClassVariable( name:String ):String {
        if (IdUtil.isValidClassVariableName(name)) {
            return name;
        }
        throw getRuntime().newNameError("`" + name + "' is not allowed as a class variable name", name);
    }

    protected function ensureClassVariablesSettable():void {
        if (!isFrozen() && (getRuntime().getSafeLevel() < 4 || isTaint())) {
            return;
        }
        
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError(ERR_INSECURE_SET_CONSTANT);
        }
        if (isFrozen()) {
            if (this instanceof RubyModule) {
                throw getRuntime().newFrozenError(ERR_FROZEN_CONST_TYPE);
            } else {
                throw getRuntime().newFrozenError("");
            }
        }
    }

    //
    ////////////////// LOW-LEVEL CONSTANT INTERFACE ////////////////
    //
    // fetch/store/list constants for this module
    //

    public function  hasConstant(name: String): Boolean {
        //assert IdUtil.isConstant(name);
        return constantTableContains(name);
    }

    public function  fastHasConstant(internedName: String): Boolean {
        //assert IdUtil.isConstant(internedName);
        return constantTableFastContains(internedName);
    }

    // returns the stored value without processing undefs (autoloads)
    public function  fetchConstant(name: String): IRubyObject {
        //assert IdUtil.isConstant(name);
        return constantTableFetch(name);
    }

    // returns the stored value without processing undefs (autoloads)
    public function  fastFetchConstant(internedName: String): IRubyObject {
        //assert IdUtil.isConstant(internedName);
        return constantTableFastFetch(internedName);
    }

    public function  storeConstant(name: String, value: IRubyObject): IRubyObject {
        //assert IdUtil.isConstant(name) && value != null;
        ensureConstantsSettable();
        return constantTableStore(name, value);
    }

    public function  fastStoreConstant(internedName: String, value: IRubyObject): IRubyObject {
        //assert IdUtil.isConstant(internedName) && value != null;
        ensureConstantsSettable();
        return constantTableFastStore(internedName, value);
    }

    // removes and returns the stored value without processing undefs (autoloads)
    public function  deleteConstant(name: String): IRubyObject {
        //assert IdUtil.isConstant(name);
        ensureConstantsSettable();
        return constantTableRemove(name);
    }

    public function  getStoredConstantList(): Array {
        var list: Array = new Array();
        var table: Array = constantTableGetTable();
		for (var i:int = table.length; --i >= 0; ) {
            for (var e:ConstantTableEntry = table[i]; e != null; e = e.next) {
                list.add(e);
            }
        }
        return list;
    }

    public function  getStoredConstantNameList(): Array {
        var list: Array = new Array();
        var table: Array = constantTableGetTable();
		for (var i:int = table.length; --i >= 0; ) {
            for (var e:ConstantTableEntry = table[i]; e != null; e = e.next) {
                list.add(e.name);
            }
        }		
        return list;
    }

 	protected static var ERR_INSECURE_SET_CONSTANT:String  = "Insecure: can't modify constant";
    protected static var ERR_FROZEN_CONST_TYPE:String  = "class/module ";
   
    protected function validateConstant( name:String):String {
        if (IdUtil.isValidConstantName(name)) {
            return name;
        }
        throw getRuntime().newNameError("wrong constant name " + name, name);
    }

    protected function ensureConstantsSettable():void {
        if (!isFrozen() && (getRuntime().getSafeLevel() < 4 || isTaint())) {
            return;
        }
        
        if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
            throw getRuntime().newSecurityError(ERR_INSECURE_SET_CONSTANT);
        }
        if (isFrozen()) {
            if (this instanceof RubyModule) {
                throw getRuntime().newFrozenError(ERR_FROZEN_CONST_TYPE);
            } else {
                throw getRuntime().newFrozenError("");
            }
        }
    }

/*     
    //
    ////////////////// VARIABLE TABLE METHODS ////////////////
    //
    // Overridden to use variableWriteLock in place of synchronization  
    //

    //@Override
    protected override function variableTableStore( name:String,  value:IRubyObject):IRubyObject {
        var hash: int = name.hashCode();       
 
        var table:Array;
        var e:VariableTableEntry;
  		if ((table = variableTable) == null) {
            table =  new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
            e = new VariableTableEntry(hash, name, value, null);
            table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
            variableTableThreshold = int(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
            variableTableSize = 1;
            variableTable = table;
            return value;
        }
        var  potentialNewSize:int;
        if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
            table = variableTableRehash();
        }
        var index:int;
        for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
            if (hash == e.hash && name.equals(e.name)) {
                e.value = value;
                return value;
            }
        }
        // external volatile value initialization intended to obviate the need for
        // readValueUnderLock technique used in ConcurrentHashMap. may be a little
        // slower, but better to pay a price on first write rather than all reads.
        e = new VariableTableEntry(hash, name, value, table[index]);
        table[index] = e;
        variableTableSize = potentialNewSize;
        variableTable = table; // write-volatile
        
        return value;
    }
    
    //@Override
    protected override function variableTableFastStore( name:String,  value:IRubyObject):IRubyObject {
        //assert internedName == internedName : internedName + " not interned";
        var hash: int = internedName.hashCode();
        
   		var table:Array;
        var e:VariableTableEntry;
  		if ((table = variableTable) == null) {
            table =  new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
            e = new VariableTableEntry(hash, internedName, value, null);
            table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
            variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
            variableTableSize = 1;
            variableTable = table;
            return value;
        }
        var potentialNewSize:int;
    	if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
                table = variableTableRehash();
        }
        var index:int;
        for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
            if (internedName == e.name) {
                e.value = value;
                return value;
            }
        }
        // external volatile value initialization intended to obviate the need for
        // readValueUnderLock technique used in ConcurrentHashMap. may be a little
        // slower, but better to pay a price on first write rather than all reads.
        e = new VariableTableEntry(hash, internedName, value, table[index]);
        table[index] = e;
        variableTableSize = potentialNewSize;
        variableTable = table; // write-volatile
    
        return value;
    }

    // @Override   
    protected override function variableTableRemove( name:String ):IRubyObject {       
        var table:Array;
        if ((table = variableTable) != null) {
            var hash:int = name.hashCode();
            var index:int = hash & (table.length - 1);
            var first:VariableTableEntry = table[index];
            var e:VariableTableEntry;
            for (e = first; e != null; e = e.next) {
                if (hash == e.hash && name.equals(e.name)) {
                    var oldValue:IRubyObject = e.value;
                    // All entries following removed node can stay
                    // in list, but all preceding ones need to be
                    // cloned.
                    var newFirst:VariableTableEntry = e.next;
                    for (var p:VariableTableEntry = first; p != e; p = p.next) {
                        newFirst = new VariableTableEntry(p.hash, p.name, p.value, newFirst);
                    }
                    table[index] = newFirst;
                    variableTableSize--;
                    variableTable = table; // write-volatile 
                    return oldValue;
                }
            }
        }
      
        return null;
    }
    
    @Override
    protected void variableTableSync(Array<Variable<IRubyObject>> vars) {
        ReentrantLock lock;
        (var lock:  = variableWriteLock).lock();
        try {
            var variableTableSize:  = 0;
            variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
            var variableTable:  =  new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
            for (Variable<IRubyObject> var : vars) {
                //assert !var.isConstant() && var.getValue() != null;
                variableTableStore(var.getName(), var.getValue());
            }
        } finally {
            lock.unlock();
        }
    }
    
    //@Override
    public function  syncVariables(variables: Array<Variable<IRubyObject>>): void {
        var constants: ArrayArray<Variable<IRubyObject>> = new ArrayArray<Variable<IRubyObject>>(variables.size());
        Variable<IRubyObject> var;
        for (var iter: Iterator<Variable<IRubyObject>> = variables.iterator(); iter.hasNext(); ) {
            var var: if = iter.next()).isConstant()) {
                constants.add(var);
                iter.remove();
            }
        }
        ReentrantLock lock;
        (var lock:  = variableWriteLock).lock();
        try {
            variableTableSync(variables);
            constantTableSync(constants);
        } finally {
            lock.unlock();
        }
    }

    //@Override
    //@SuppressWarnings("unchecked")
    //@Deprecated // born deprecated
    public override function  getVariableMap(): Dictionary {
        var map: Map = variableTableGetMap();
        constantTableGetMap(map);
        return map;
    }

    //@Override
    public override function  hasVariables(): Boolean {
        return variableTableGetSize() > 0 || constantTableGetSize() > 0;
    }

    //@Override
    public override function  getVariableCount(): int {
        return variableTableGetSize() + constantTableGetSize();
    }
    
    //@Override
    public override function  getVariableList(): Array {
        var vtable: Array = variableTableGetTable();
        var ctable: Array = constantTableGetTable();
        var list: Array = new Array();
        var readValue:IRubyObject;
        for (var i:int = vtable.length; --i >= 0; ) {
            for (var e:VariableTableEntry = vtable[i]; e != null; e = e.next) {
                if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
                list.add(new VariableEntry<IRubyObject>(e.name, readValue));
            }
        }
        for (var i:int = ctable.length; --i >= 0; ) {
            for (var ee:ConstantTableEntry = ctable[i]; ee != null; ee = ee.next) {
                list.add(ee);
            }
        }
        return list;
    }

    //@Override
    public override function  getVariableNameList(): Array {
        var vtable: Array = variableTableGetTable();
        var ctable: Array = constantTableGetTable();
        var list: Array = new Array();
        for (var i:int = vtable.length; --i >= 0; ) {
            for (var e:VariableTableEntry = vtable[i]; e != null; e = e.next) {
                list.add(e.name);
            }
        }
        for (var i:int = ctable.length; --i >= 0; ) {
            for (var ee:ConstantTableEntry = ctable[i]; ee != null; ee = ee.next) {
                list.add(ee.name);
            }
        }
        return list;
    }
 

    //
    ////////////////// CONSTANT TABLE METHODS, ETC. ////////////////
    //
    
    protected static  var CONSTANT_TABLE_DEFAULT_CAPACITY: int = 8; // MUST be power of 2!
    protected static  var CONSTANT_TABLE_MAXIMUM_CAPACITY: int = 1 << 30;
    protected static  var CONSTANT_TABLE_LOAD_FACTOR: Number = 0.75;
  
    protected function constantTableContains( name:String):Boolean {
        var hash: int = name.hashCode();
        var table: Array;
 		for (var e:ConstantTableEntry = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
            if (hash == e.hash && name.equals(e.name)) {
                return true;
            }
        }
        return false;
    }
    
    protected function constantTableFastContains(internedName:String):Boolean {
        var table:Array;
     	for (var e:ConstantTableEntry = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
            if (internedName == e.name) {
                return true;
            }
        }
        return false;
    }
    
    protected function constantTableFetch( name:String ):IRubyObject {
        var hash: int = name.hashCode();
        var table: Array;
        for (var e:ConstantTableEntry = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
            if (hash == e.hash && name.equals(e.name)) {
                return e.value;
            }
        }
        return null;
    }
    
    protected function constantTableFastFetch( internedName:String ):IRubyObject {
        var table:Array;
 		for (var e:ConstantTableEntry = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
            if (internedName == e.name) {
                return e.value;
            }
        }
        return null;
    }
    
    protected function constantTableStore(name:String,  value:IRubyObject):IRubyObject {
        var hash: int = name.hashCode();
        var table:Array;
        var e:ConstantTableEntry;
        var first:ConstantTableEntry;
        var potentialNewSize:int;
        if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
            table = constantTableRehash();
        } else {
            table = constantTable;
        }
        var index:int;
  		for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
            if (hash == e.hash && name.equals(e.name)) {
                // if value is unchanged, do nothing
                if (value == e.value) {
                    return value;
                }
                // create new entry, prepend to any trailing entries
                var newFirst:ConstantTableEntry = new ConstantTableEntry(e.hash, e.name, value, e.next);
                // all entries before this one must be cloned
                for (var n:ConstantTableEntry = first; n != e; n = n.next) {
                    newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
                }
                table[index] = newFirst;
                constantTable = table; // write-volatile
                return value;
            }
        }
        table[index] = new ConstantTableEntry(hash, name, value, table[index]);
        constantTableSize = potentialNewSize;
        constantTable = table; // write-volatile
        
        return value;
    }
    
    protected function constantTableFastStore(internedName:String, value:IRubyObject):IRubyObject {
        //assert internedName == internedName : internedName + " not interned";
        var hash: int = internedName.hashCode();
 		var table:Array;
        var e:ConstantTableEntry;
        var first:ConstantTableEntry;
        var potentialNewSize:int;
  		if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
            table = constantTableRehash();
        } else {
            table = constantTable;
        }
        var index:int;
        for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
            if (internedName == e.name) {
                // if value is unchanged, do nothing
                if (value == e.value) {
                    return value;
                }
                // create new entry, prepend to any trailing entries
                var newFirst:ConstantTableEntry = new ConstantTableEntry(e.hash, e.name, value, e.next);
                // all entries before this one must be cloned
                for (var n:ConstantTableEntry = first; n != e; n = n.next) {
                    newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
                }
                table[index] = newFirst;
                constantTable = table; // write-volatile
                return value;
            }
        }
        table[index] = new ConstantTableEntry(hash, internedName, value, table[index]);
        constantTableSize = potentialNewSize;
        constantTable = table; // write-volatile
        return value;
    }
        
    protected function constantTableRemove( name:String ):IRubyObject {   
        var table:Array;
 		if ((table = constantTable) != null) {
            var hash:int = name.hashCode();
            var index:int = hash & (table.length - 1);
            var first:ConstantTableEntry = table[index];
            var e:ConstantTableEntry;
            for (e = first; e != null; e = e.next) {
                if (hash == e.hash && name.equals(e.name)) {
                    var oldValue:IRubyObject = e.value;
                    // All entries following removed node can stay
                    // in list, but all preceding ones need to be
                    // cloned.
                    var newFirst:ConstantTableEntry = e.next;
                    for (var p:ConstantTableEntry = first; p != e; p = p.next) {
                        newFirst = new ConstantTableEntry(p.hash, p.name, p.value, newFirst);
                    }
                    table[index] = newFirst;
                    constantTableSize--;
                    constantTable = table; // write-volatile 
                    return oldValue;
                }
            }
        }
   
        return null;
    }
    

    protected function constantTableGetTable():Array {
        return constantTable;
    }
    
    protected function constantTableGetSize():int {
        if (constantTable != null) {
            return constantTableSize;
        }
        return 0;
    }
    
    protected function constantTableSync(vars:Array):void {
  		constantTableSize = 0;
  		constantTableThreshold = (int)(CONSTANT_TABLE_DEFAULT_CAPACITY * CONSTANT_TABLE_LOAD_FACTOR);
        constantTable =  new Array();
        for (var vari:Variable in vars) {
            //assert var.isConstant() && vari.getValue() != null;
            constantTableStore(vari.getName(), vari.getValue());
        }
    }
    
    // MUST be called from synchronized/locked block!
    // should only be called by constantTableStore/constantTableFastStore
    private function  constantTableRehash(): Array {
        var oldTable: Array = constantTable;
        var oldCapacity:int;
    	if ((oldCapacity = oldTable.length) >= CONSTANT_TABLE_MAXIMUM_CAPACITY) {
            return oldTable;
        }
        
        var newCapacity: int = oldCapacity << 1;
        var newTable: Array = new Array;
        constantTableThreshold = (int)(newCapacity * CONSTANT_TABLE_LOAD_FACTOR);
        var sizeMask: int = newCapacity - 1;
        var e:ConstantTableEntry;
 		for (var i:int = oldCapacity; --i >= 0; ) {
            // We need to guarantee that any existing reads of old Map can
            //  proceed. So we cannot yet null out each bin.
            e = oldTable[i];

            if (e != null) {
                var next:ConstantTableEntry = e.next;
                var idx:int = e.hash & sizeMask;

                //  Single node on list
                if (next == null)
                    newTable[idx] = e;

                else {
                    // Reuse trailing consecutive sequence at same slot
                    var lastRun:ConstantTableEntry = e;
                    var lastIdx:int = idx;
                    for (var last:ConstantTableEntry = next;
                         last != null;
                         last = last.next) {
                        var k:int = last.hash & sizeMask;
                        if (k != lastIdx) {
                            lastIdx = k;
                            lastRun = last;
                        }
                    }
                    newTable[lastIdx] = lastRun;

                    // Clone all remaining nodes
                    for (var p:ConstantTableEntry = e; p != lastRun; p = p.next) {
                        var k:int = p.hash & sizeMask;
                        var m:ConstantTableEntry = new ConstantTableEntry(p.hash, p.name, p.value, newTable[k]);
                        newTable[k] = m;
                    }
                }
            }
        }
        constantTable = newTable;
        return newTable;
    }
    

    // Method to help ease transition to new variables implementation.
    // Will likely be deprecated in the near future.
    //@SuppressWarnings("unchecked")
    protected function constantTableGetMap():Dictionary {
        var map: Dictionary = new Dictionary();
        var table:Array;
        if ((table = constantTable) != null) {
            for (var i:int = table.length; --i >= 0; ) {
                for (var e:ConstantTableEntry = table[i]; e != null; e = e.next) {
                    map.put(e.name, e.value);
                }
            }
        }
        return map;
    }
    
    // Method to help ease transition to new variables implementation.
    //Will likely be deprecated in the near future.

    //@SuppressWarnings("unchecked")
    protected function constantTableGetMap2(map:Dictionary):Dictionary {
        var table:Array;
        if ((table = constantTable) != null) {
            for (var i:int = table.length; --i >= 0; ) {
                for (var e:ConstantTableEntry = table[i]; e != null; e = e.next) {
                    map.put(e.name, e.value);
                }
            }
        }
        return map;
    }
*/   
    public function getAttached():RubyModule{
    	return null;
    }
    public function constantTableFastFetch( internedName:String):IRubyObject{
    	var table:Array;
    	var e:ConstantTableEntry;
       /*  for ( e = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
            if (internedName == e.name) {
                return e.value;
            }
        } */
        return null;
    }
    public function constantTableFetch():IRubyObject{
    	return null;
    }

}

}